home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / DirectInput / MultiMapper / multidi.cpp next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  32.8 KB  |  929 lines

  1. //-----------------------------------------------------------------------------
  2. // File: MultiDI.cpp
  3. //
  4. // Desc: DirectInput framework class using semantic mapping with multiplayer
  5. //       device ownership.  Feel free to use this class as a starting point 
  6. //       for adding extra functionality.
  7. //
  8. // Copyright (C) Microsoft Corporation. All Rights Reserved.
  9. //-----------------------------------------------------------------------------
  10. #define STRICT
  11. #define DIRECTINPUT_VERSION 0x0800
  12. #include <basetsd.h>
  13. #include <tchar.h>
  14. #include <stdio.h>
  15. #include <windows.h>
  16. #include <dxerr9.h>
  17. #include <d3d8types.h>  // included to get the D3DCOLOR_RGBA macro.
  18. #include "MultiDI.h"
  19. #include <assert.h>
  20.  
  21.  
  22.  
  23.  
  24. //-----------------------------------------------------------------------------
  25. // Name: CMultiplayerInputDeviceManager
  26. // Desc: Constructor
  27. // Args: strRegKey - A location in the registry where device ownership 
  28. //       information should be stored
  29. //-----------------------------------------------------------------------------
  30. CMultiplayerInputDeviceManager::CMultiplayerInputDeviceManager( TCHAR* strRegKey )
  31. {
  32.     HRESULT hr = CoInitialize(NULL);
  33.     m_bCleanupCOM = SUCCEEDED(hr);
  34.     LONG nResult;
  35.  
  36.     // Initialize members
  37.     m_pDI                       = NULL;
  38.     m_hWnd                      = NULL;
  39.     m_pdiaf                     = NULL;
  40.     m_pUsers                    = NULL;
  41.     m_pDeviceList               = NULL;
  42.     m_AddDeviceCallback         = NULL;
  43.     m_AddDeviceCallbackParam    = NULL;
  44.     m_hKey                      = NULL;
  45.     m_dwNumDevices              = 0;
  46.     m_dwMaxDevices              = 0; 
  47.     
  48.  
  49.     // Duplicate the registry location string since we'll need this again later
  50.     m_strKey = _tcsdup( strRegKey );
  51.     if( m_strKey == NULL )
  52.         return;
  53.  
  54.     // Create a reg key to store device ownership data 
  55.     nResult = RegCreateKeyEx( HKEY_CURRENT_USER, strRegKey, 0, NULL,
  56.                               REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, 
  57.                               &m_hKey, NULL );
  58.     if(nResult != ERROR_SUCCESS)
  59.         m_hKey = NULL;
  60. }
  61.  
  62.  
  63.  
  64.  
  65. //-----------------------------------------------------------------------------
  66. // Name: ~CMultiplayerInputDeviceManager
  67. // Desc: Destructor
  68. //-----------------------------------------------------------------------------
  69. CMultiplayerInputDeviceManager::~CMultiplayerInputDeviceManager()
  70. {
  71.     Cleanup();
  72.     
  73.     if( m_bCleanupCOM )
  74.         CoUninitialize();
  75.  
  76.     RegCloseKey( m_hKey );
  77.  
  78.     free( m_strKey );
  79. }
  80.  
  81.  
  82.  
  83.  
  84. //-----------------------------------------------------------------------------
  85. // Name: Create
  86. // Desc: Initializes the class, and enums the devices.  See MultiMapper sample
  87. //       for how to use this class.
  88. //       It might fail if there are too many players for the 
  89. //       number of devices availible, or if one player owns too many
  90. //       devices preventing others from having a device.  Its up the app
  91. //       to prevent this or respond to this.
  92. //       Note: strUserName should be a array of sz strings 
  93. //-----------------------------------------------------------------------------
  94. HRESULT CMultiplayerInputDeviceManager::Create( HWND hWnd, 
  95.                                      TCHAR* strUserNames[], 
  96.                                      DWORD dwNumUsers,
  97.                                      DIACTIONFORMAT* pdiaf,
  98.                                      LPDIMANAGERCALLBACK AddDeviceCallback, 
  99.                                      LPVOID pCallbackParam,
  100.                                      BOOL bResetOwnership, 
  101.                                      BOOL bResetMappings )
  102. {
  103.     HRESULT hr;
  104.     
  105.     if( strUserNames == NULL || dwNumUsers == 0 )
  106.         return E_INVALIDARG;
  107.  
  108.     Cleanup();
  109.     
  110.     // Store data
  111.     m_hWnd = hWnd;
  112.  
  113.     // Create and init the m_pUsers array 
  114.     m_dwNumUsers = dwNumUsers;
  115.     m_pUsers     = new PlayerInfo*[dwNumUsers];
  116.     for( DWORD i=0; i<dwNumUsers; i++ )
  117.     {
  118.         m_pUsers[i] = new PlayerInfo;
  119.         ZeroMemory( m_pUsers[i], sizeof(PlayerInfo) );        
  120.         m_pUsers[i]->dwPlayerIndex = i; // set the 0-based player index (for easy referencing)
  121.         lstrcpyn( m_pUsers[i]->strPlayerName, strUserNames[i], MAX_PATH-1 );            
  122.     }
  123.     
  124.     m_AddDeviceCallback = AddDeviceCallback;
  125.     m_AddDeviceCallbackParam = pCallbackParam;
  126.  
  127.     // Create the base DirectInput object
  128.     if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, 
  129.                                          IID_IDirectInput8, (VOID**)&m_pDI, NULL ) ) )
  130.         return DXTRACE_ERR( TEXT("DirectInput8Create"), hr );
  131.     
  132.     if( FAILED( hr = SetActionFormat( pdiaf, TRUE, bResetOwnership, bResetMappings ) ) )
  133.         return DXTRACE_ERR( TEXT("SetActionFormat"), hr );
  134.     
  135.     return S_OK;
  136. }
  137.  
  138.  
  139.  
  140.  
  141. //-----------------------------------------------------------------------------
  142. // Name: SetActionFormat
  143. // Desc: Sets a new action format.  
  144. //       It re-enumerates the devices if bReenumerate
  145. //       It resets the ownership of the devices if bResetOwnership
  146. //       It resets the mapper actions of the devices if bResetMappings
  147. //       This function may fail if there are too many players for the 
  148. //       number of devices availible, or if one player owns too many
  149. //       devices preventing others from having a device.  Its up the app
  150. //       to prevent this or respond to this.
  151. //-----------------------------------------------------------------------------
  152. HRESULT CMultiplayerInputDeviceManager::SetActionFormat( DIACTIONFORMAT* pdiaf, 
  153.                                               BOOL bReenumerate, 
  154.                                               BOOL bResetOwnership,
  155.                                               BOOL bResetMappings )
  156. {
  157.     HRESULT hr;
  158.     DWORD iPlayer;
  159.     DWORD iDevice;
  160.  
  161.     // Store the new action format
  162.     m_pdiaf = pdiaf;
  163.     
  164.     // Only destroy and re-enumerate devices if the caller explicitly wants to.
  165.     // This isn't thread safe, so be sure not to have any other threads using
  166.     // this data unless you redesign this class
  167.     if( bReenumerate )
  168.     {
  169.         // Set all players to not have a device yet
  170.         for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
  171.             m_pUsers[iPlayer]->bFoundDeviceForPlayer = FALSE;
  172.  
  173.         if( bResetOwnership )
  174.         {
  175.             // Set all devices as not assigned to a player
  176.             for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  177.                 m_pDeviceList[iDevice].pPlayerInfo = NULL;
  178.  
  179.             // Delete the device ownership keys
  180.             DeleteDeviceOwnershipKeys();
  181.         }
  182.  
  183.         // Devices must be unacquired to have a new action map set.
  184.         UnacquireDevices();
  185.         
  186.         // Enumerate all available devices that map the pri 1 actions,
  187.         // and store them in the m_pDeviceList array
  188.         if( FAILED( hr = BuildDeviceList() ) )
  189.             return DXTRACE_ERR( TEXT("BuildDeviceList"), hr );
  190.         
  191.         // Assign devices to any players that don't have devices
  192.         // Some games may want to change this functionality.
  193.         if( FAILED( hr = AssignDevices() ) )
  194.             return DXTRACE_ERR( TEXT("AssignDevices"), hr );
  195.  
  196.         // Report an error if there are too many players for the 
  197.         // number of devices availible, or if one player owns too many
  198.         // devices preventing others from having a device
  199.         if( FAILED( hr = VerifyAssignment() ) )
  200.             return hr;
  201.  
  202.         // Now that every player has at least one device, build and set 
  203.         // the action map, and use callback into the app to tell the 
  204.         // app of the device assignment.
  205.         if( FAILED( hr = AddAssignedDevices( bResetMappings ) ) )
  206.             return DXTRACE_ERR( TEXT("AddAssignedDevices"), hr );
  207.  
  208.         // For every device that's assigned to a player, save its device key 
  209.         // and assigned player to registry, so the app remembers which devices
  210.         // each player prefers
  211.         if( FAILED( hr = SaveDeviceOwnershipKeys() ) )
  212.             return DXTRACE_ERR( TEXT("SaveDeviceOwnershipKeys"), hr );
  213.     }
  214.     else
  215.     {
  216.         // Just apply the new maps for each device owned by each user
  217.         
  218.         // Devices must be unacquired to have a new action map set.
  219.         UnacquireDevices();
  220.         
  221.         // Apply the new action map to the current devices.
  222.         for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  223.         {
  224.             LPDIRECTINPUTDEVICE8 pdidDevice = m_pDeviceList[iDevice].pdidDevice;
  225.             PlayerInfo* pPlayerInfo = m_pDeviceList[iDevice].pPlayerInfo;
  226.  
  227.             if( FAILED( hr = pdidDevice->BuildActionMap( m_pdiaf, pPlayerInfo->strPlayerName, DIDBAM_DEFAULT ) ) )
  228.                 return DXTRACE_ERR( TEXT("BuildActionMap"), hr );
  229.             if( FAILED( hr = pdidDevice->SetActionMap( m_pdiaf, pPlayerInfo->strPlayerName, DIDSAM_DEFAULT ) ) )
  230.                 return DXTRACE_ERR( TEXT("SetActionMap"), hr );
  231.         }
  232.     }
  233.  
  234.     return S_OK;
  235. }
  236.  
  237.  
  238.  
  239.  
  240. //-----------------------------------------------------------------------------
  241. // Name: BuildDeviceList
  242. // Desc:
  243. //-----------------------------------------------------------------------------
  244. HRESULT CMultiplayerInputDeviceManager::BuildDeviceList()
  245. {
  246.     // Cleanup any previously enumerated devices
  247.     CleanupDeviceList();
  248.     
  249.     // Build a simple list of all devices currently attached to the machine. This
  250.     // array will be used to reassign devices to each user.
  251.     m_dwMaxDevices = 5;
  252.     m_dwNumDevices = 0;
  253.     m_pDeviceList = NULL;
  254.  
  255.     DeviceInfo* pListNew = NULL;
  256.     pListNew = (DeviceInfo*) realloc( m_pDeviceList, m_dwMaxDevices*sizeof(DeviceInfo) );
  257.     
  258.     // Verify allocation
  259.     if( NULL == pListNew )
  260.         return DXTRACE_ERR( TEXT("BuildDeviceList"), E_OUTOFMEMORY );
  261.     else
  262.         m_pDeviceList = pListNew;
  263.  
  264.      ZeroMemory( m_pDeviceList, m_dwMaxDevices*sizeof(DeviceInfo) );        
  265.  
  266.     // Enumerate available devices for any user.
  267.     m_pDI->EnumDevicesBySemantics( NULL, m_pdiaf, StaticEnumSuitableDevicesCB, 
  268.                                    this, DIEDBSFL_ATTACHEDONLY );
  269.  
  270.     return S_OK;
  271. }
  272.  
  273.  
  274.  
  275.  
  276. //-----------------------------------------------------------------------------
  277. // Name: EnumSuitableDevicesCB
  278. // Desc: DirectInput device enumeratation callback.  Calls AddDevice()
  279. //       on each device enumerated.
  280. //-----------------------------------------------------------------------------
  281. BOOL CALLBACK CMultiplayerInputDeviceManager::StaticEnumSuitableDevicesCB( LPCDIDEVICEINSTANCE pdidi, 
  282.                                                                LPDIRECTINPUTDEVICE8 pdidDevice, 
  283.                                                                DWORD dwFlags, DWORD dwDeviceRemaining,
  284.                                                                VOID* pContext )
  285. {
  286.     // Add the device to the device manager's internal list
  287.     CMultiplayerInputDeviceManager* pInputDeviceManager = (CMultiplayerInputDeviceManager*)pContext;
  288.     return pInputDeviceManager->EnumDevice( pdidi, pdidDevice, 
  289.                                             dwFlags, dwDeviceRemaining );    
  290. }
  291.  
  292.  
  293.  
  294.  
  295. //-----------------------------------------------------------------------------
  296. // Name: EnumDevice
  297. // Desc: Enums each device to see if its suitable to add 
  298. //-----------------------------------------------------------------------------
  299. BOOL CMultiplayerInputDeviceManager::EnumDevice( const DIDEVICEINSTANCE* pdidi, 
  300.                                      const LPDIRECTINPUTDEVICE8 pdidDevice,
  301.                                      DWORD dwFlags, DWORD dwRemainingDevices )
  302. {    
  303.     TCHAR strPlayerName[MAX_PATH];
  304.     TCHAR strDeviceGuid[40];
  305.     
  306.     // Devices of type DI8DEVTYPE_DEVICECTRL are specialized devices not generally
  307.     // considered appropriate to control game actions. We just ignore these.
  308.     if( GET_DIDEVICE_TYPE(pdidi->dwDevType) != DI8DEVTYPE_DEVICECTRL )
  309.     {
  310.         // We're only interested in devices that map the pri 1 actions
  311.         if( dwFlags & DIEDBS_MAPPEDPRI1 )
  312.         {
  313.             // Add new pdidDevice struct to array, and resize array if needed
  314.             m_dwNumDevices++;
  315.             if( m_dwNumDevices > m_dwMaxDevices )
  316.             {
  317.                 m_dwMaxDevices += 5;
  318.  
  319.                 DeviceInfo* pListNew = NULL;
  320.                 pListNew = (DeviceInfo*) realloc( m_pDeviceList, m_dwMaxDevices*sizeof(DeviceInfo) );
  321.     
  322.                 // Verify allocation
  323.                 if( NULL == pListNew )
  324.                 {
  325.                     DXTRACE_ERR( TEXT("EnumDevice"), E_OUTOFMEMORY );
  326.                     return DIENUM_STOP;
  327.                 }
  328.                 else
  329.                     m_pDeviceList = pListNew;
  330.  
  331.                 ZeroMemory( m_pDeviceList + m_dwMaxDevices - 5, 5*sizeof(DeviceInfo) );
  332.             }
  333.             
  334.             DXUtil_ConvertGUIDToStringCch( &pdidi->guidInstance, strDeviceGuid, 40 );
  335.             DXUtil_ReadStringRegKeyCch( m_hKey, strDeviceGuid, strPlayerName, MAX_PATH, TEXT("") );        
  336.             
  337.             // Add the device to the array m_pDeviceList
  338.             DWORD dwCurrentDevice = m_dwNumDevices-1;
  339.             ZeroMemory( &m_pDeviceList[dwCurrentDevice], sizeof(DeviceInfo) );
  340.  
  341.             m_pDeviceList[dwCurrentDevice].didi = *pdidi;            
  342.             m_pDeviceList[dwCurrentDevice].pdidDevice = pdidDevice;        
  343.             m_pDeviceList[dwCurrentDevice].pdidDevice->AddRef();
  344.             m_pDeviceList[dwCurrentDevice].bMapsPri1Actions = ((dwFlags & DIEDBS_MAPPEDPRI1) == DIEDBS_MAPPEDPRI1);
  345.             m_pDeviceList[dwCurrentDevice].bMapsPri2Actions = ((dwFlags & DIEDBS_MAPPEDPRI2) == DIEDBS_MAPPEDPRI2);
  346.             
  347.             if( lstrcmp( strPlayerName, TEXT("") ) != 0 )
  348.             {
  349.                 m_pDeviceList[dwCurrentDevice].pPlayerInfo = LookupPlayer( strPlayerName );
  350.                 if( m_pDeviceList[dwCurrentDevice].pPlayerInfo )
  351.                     m_pDeviceList[dwCurrentDevice].pPlayerInfo->bFoundDeviceForPlayer = TRUE;
  352.             }
  353.         }
  354.     }
  355.     
  356.     // Continue enumerating 
  357.     return DIENUM_CONTINUE;
  358. }
  359.  
  360.  
  361.  
  362.  
  363. //-----------------------------------------------------------------------------
  364. // Name: AssignDevices
  365. // Desc:
  366. //-----------------------------------------------------------------------------
  367. HRESULT CMultiplayerInputDeviceManager::AssignDevices()
  368. {    
  369.     DWORD iDevice;
  370.     DWORD iPlayer;
  371.  
  372.     // For any device that doesn't have a user assigned to it,
  373.     // then assign it to the first user that needs a device
  374.     for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  375.     {
  376.         if( m_pDeviceList[iDevice].pPlayerInfo == NULL )
  377.         {
  378.             for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
  379.             {
  380.                 if( !m_pUsers[iPlayer]->bFoundDeviceForPlayer )
  381.                 {
  382.                     m_pDeviceList[iDevice].pPlayerInfo = m_pUsers[iPlayer];
  383.                     m_pUsers[iPlayer]->bFoundDeviceForPlayer = TRUE;
  384.                     break;
  385.                 }
  386.             }                        
  387.         }
  388.     }
  389.  
  390.     return S_OK;
  391. }
  392.  
  393.  
  394.  
  395.  
  396. //-----------------------------------------------------------------------------
  397. // Name: VerifyAssignment
  398. // Desc:
  399. //-----------------------------------------------------------------------------
  400. HRESULT CMultiplayerInputDeviceManager::VerifyAssignment()
  401. {    
  402.     DWORD iPlayer;
  403.     DWORD iDevice;
  404.  
  405.     // For each player, make sure that a device was found for this 
  406.     // player, otherwise return failure.
  407.     for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
  408.     {
  409.         if( !m_pUsers[iPlayer]->bFoundDeviceForPlayer )
  410.         {
  411.             if( GetNumDevices() < m_dwNumUsers )
  412.                 return E_DIUTILERR_TOOMANYUSERS;
  413.             else
  414.             {
  415.                 // Check to see if there's a device that isn't already
  416.                 // assigned to a player. If there is return 
  417.                 // E_DIUTILERR_PLAYERWITHOUTDEVICE otherwise return 
  418.                 // E_DIUTILERR_DEVICESTAKEN
  419.                 for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  420.                 {
  421.                     if( m_pDeviceList[iDevice].pPlayerInfo == NULL )
  422.                         return E_DIUTILERR_PLAYERWITHOUTDEVICE;
  423.                 }
  424.                 
  425.                 return E_DIUTILERR_DEVICESTAKEN;
  426.             }
  427.         }                       
  428.     }
  429.  
  430.     return S_OK;
  431. }
  432.  
  433.  
  434.  
  435.  
  436. //-----------------------------------------------------------------------------
  437. // Name: AddAssignedDevices
  438. // Desc: For every device that's assigned to a player, set it action map
  439. //       and add it to the game
  440. //-----------------------------------------------------------------------------
  441. HRESULT CMultiplayerInputDeviceManager::AddAssignedDevices( BOOL bResetMappings )
  442. {    
  443.     DWORD iDevice;
  444.     DWORD iAction;
  445.     HRESULT hr = S_OK;
  446.     DWORD *pdwAppFixed = NULL;
  447.  
  448.     // If flagged, we'll be remapping the actions to hardware defaults, and in
  449.     // the process DirectInput will clear the DIA_APPFIXED flag, so we need 
  450.     // to make a copy in order to reapply the flag later.
  451.     if( bResetMappings )
  452.     {
  453.         pdwAppFixed = new DWORD[m_pdiaf->dwNumActions];
  454.         
  455.         // Verify memory allocation and collect DIA_APPFIXED settings
  456.         if( pdwAppFixed )
  457.         {
  458.             for( iAction=0; iAction < m_pdiaf->dwNumActions; iAction++ )
  459.                 pdwAppFixed[iAction] = m_pdiaf->rgoAction[iAction].dwFlags & DIA_APPFIXED;
  460.         }
  461.     }
  462.  
  463.     // For every device that's assigned to a player, 
  464.     // set it action map, and add it to the game
  465.     for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  466.     {                
  467.         LPDIRECTINPUTDEVICE8 pdidDevice = m_pDeviceList[iDevice].pdidDevice;
  468.         PlayerInfo*          pPlayerInfo = m_pDeviceList[iDevice].pPlayerInfo;           
  469.         
  470.         if( pPlayerInfo != NULL )
  471.         {   
  472.             // Set the device's coop level
  473.             hr = pdidDevice->SetCooperativeLevel( m_hWnd, DISCL_NONEXCLUSIVE|DISCL_FOREGROUND );
  474.             if( FAILED(hr) )
  475.                 break;
  476.             
  477.             // Build and set the action map on this device.  This will also remove 
  478.             // it from DirectInput's internal list of available devices.
  479.             DWORD dwBuildFlags = bResetMappings ? DIDBAM_HWDEFAULTS : DIDBAM_DEFAULT;   
  480.               DWORD dwSetFlags = bResetMappings ? DIDSAM_FORCESAVE : DIDSAM_DEFAULT;
  481.  
  482.             hr = pdidDevice->BuildActionMap( m_pdiaf, pPlayerInfo->strPlayerName, dwBuildFlags );
  483.             if( FAILED( hr ) )
  484.             {
  485.                 // just print out a debug message and keep going
  486.                 DXTRACE_ERR( TEXT("BuildActionMap"), hr );
  487.                 hr = S_OK;
  488.                 continue;
  489.             }
  490.  
  491.             hr = pdidDevice->SetActionMap( m_pdiaf, pPlayerInfo->strPlayerName, dwSetFlags );
  492.             if( FAILED( hr ) )
  493.             {
  494.                 // just print out a debug message and keep going
  495.                 DXTRACE_ERR( TEXT("SetActionMap"), hr );   
  496.                 hr = S_OK;
  497.                 continue;
  498.             }
  499.             
  500.  
  501.             // Callback into the app so it can adjust the device and set
  502.             // the m_pDeviceList[iDevice].pParam field with a device state struct
  503.             if( m_AddDeviceCallback )
  504.                 m_AddDeviceCallback( pPlayerInfo, &m_pDeviceList[iDevice], 
  505.                                      &m_pDeviceList[iDevice].didi, m_AddDeviceCallbackParam );            
  506.             
  507.             // Check to see if the device is using relative axis -- sometimes app code
  508.             // might want to know this.
  509.             DIPROPDWORD dipdw;
  510.             dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
  511.             dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  512.             dipdw.diph.dwObj        = 0;
  513.             dipdw.diph.dwHow        = DIPH_DEVICE;
  514.             dipdw.dwData            = 0;
  515.             pdidDevice->GetProperty( DIPROP_AXISMODE, &dipdw.diph );
  516.             if( dipdw.dwData == DIPROPAXISMODE_REL )
  517.                 m_pDeviceList[iDevice].bRelativeAxis = TRUE;   
  518.             
  519.             // We made it through this iteration without breaking out do to errors
  520.             hr = S_OK;
  521.         }
  522.         else
  523.         {
  524.             if( FAILED( hr = pdidDevice->BuildActionMap( m_pdiaf, NULL, DIDBAM_DEFAULT ) ) )
  525.             {
  526.                 DXTRACE_ERR( TEXT("BuildActionMap"), hr );
  527.                 break;
  528.             }
  529.             
  530.             if( FAILED( hr = pdidDevice->SetActionMap( m_pdiaf, NULL, DIDSAM_NOUSER ) ) )
  531.             {
  532.                 DXTRACE_ERR( TEXT("SetActionMap"), hr );   
  533.                 break;
  534.             }
  535.         }
  536.     }                              
  537.  
  538.     // If we stored DIA_APPFIXED flags earlier, we need to reapply those flags and
  539.     // free the allocated memory
  540.     if( bResetMappings && pdwAppFixed )
  541.     {
  542.         for( iAction=0; iAction < m_pdiaf->dwNumActions; iAction++ )
  543.             m_pdiaf->rgoAction[iAction].dwFlags |= pdwAppFixed[iAction];
  544.         
  545.         delete [] pdwAppFixed;
  546.     }
  547.  
  548.     return hr;
  549. }
  550.  
  551.  
  552.  
  553.  
  554. //-----------------------------------------------------------------------------
  555. // Name: ConfigureDevices
  556. // Desc:
  557. //-----------------------------------------------------------------------------
  558. HRESULT CMultiplayerInputDeviceManager::ConfigureDevices( HWND hWnd, IUnknown* pSurface,
  559.                                                VOID* ConfigureDevicesCB,
  560.                                                DWORD dwFlags, LPVOID pvCBParam )
  561. {
  562.     HRESULT hr;
  563.     DWORD iPlayer;
  564.     
  565.     // Determine how large of a string we'll need to hold all user names
  566.     DWORD dwNamesSize = 0;
  567.     for( iPlayer=0; iPlayer < m_dwNumUsers; iPlayer++ )
  568.         dwNamesSize += lstrlen( m_pUsers[iPlayer]->strPlayerName ) +1;
  569.  
  570.     // Build multi-sz list of user names
  571.     TCHAR* strUserNames = new TCHAR[dwNamesSize+1];
  572.     
  573.     // Verify allocation and cycle through user names
  574.     if( strUserNames ) 
  575.     {
  576.         TCHAR* strTemp = strUserNames;
  577.         for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
  578.         {
  579.             lstrcpy( strTemp, m_pUsers[iPlayer]->strPlayerName );
  580.             strTemp += lstrlen(strTemp) + 1;
  581.         }
  582.  
  583.         lstrcpy( strTemp, TEXT("\0") );
  584.     }
  585.  
  586.     
  587.     // Fill in all the params
  588.     DICONFIGUREDEVICESPARAMS dicdp;
  589.     ZeroMemory(&dicdp, sizeof(dicdp));
  590.     dicdp.dwSize = sizeof(dicdp);
  591.     dicdp.dwcFormats     = 1;
  592.     dicdp.lprgFormats    = m_pdiaf;
  593.     dicdp.hwnd           = hWnd;
  594.     dicdp.lpUnkDDSTarget = pSurface;
  595.     dicdp.dwcUsers       = m_dwNumUsers;
  596.     dicdp.lptszUserNames = strUserNames;
  597.  
  598.     // Initialize all the colors here
  599.     DICOLORSET dics;
  600.     ZeroMemory(&dics, sizeof(DICOLORSET));
  601.     dics.dwSize = sizeof(DICOLORSET);
  602.  
  603.     // Set UI color scheme (if not specified it uses defaults)
  604.     dicdp.dics.dwSize = sizeof(dics);
  605.     dicdp.dics.cTextFore        = D3DCOLOR_RGBA(255,255,255,255);
  606.     dicdp.dics.cTextHighlight   = D3DCOLOR_RGBA(60,191,241,255);
  607.     dicdp.dics.cCalloutLine     = D3DCOLOR_RGBA(255,255,255,128);
  608.     dicdp.dics.cCalloutHighlight= D3DCOLOR_RGBA(60,191,241,255);
  609.     dicdp.dics.cBorder          = D3DCOLOR_RGBA(140,152,140,128);
  610.     dicdp.dics.cControlFill     = D3DCOLOR_RGBA(113,0,0,128);
  611.     dicdp.dics.cHighlightFill   = D3DCOLOR_RGBA(0,0,0,128);
  612.     dicdp.dics.cAreaFill        = D3DCOLOR_RGBA(0,0,0,128);
  613.         
  614.     if( dwFlags & DICD_EDIT )
  615.     {
  616.         // Re-enum so we can catch any new devices that have been recently attached
  617.         for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
  618.             m_pUsers[iPlayer]->bFoundDeviceForPlayer = FALSE;
  619.         if( FAILED( hr = BuildDeviceList() ) )
  620.         {
  621.             DXTRACE_ERR( TEXT("BuildDeviceList"), hr );
  622.             goto LCleanup;
  623.         }
  624.     }
  625.         
  626.     // Unacquire the devices so that mouse doesn't 
  627.     // control the game while in control panel
  628.     UnacquireDevices();
  629.  
  630.     if( FAILED( hr = m_pDI->ConfigureDevices( (LPDICONFIGUREDEVICESCALLBACK)ConfigureDevicesCB, 
  631.                                   &dicdp, dwFlags, pvCBParam ) ) )
  632.     {
  633.         DXTRACE_ERR( TEXT("ConfigureDevices"), hr );   
  634.         goto LCleanup;
  635.     }
  636.  
  637.     if( dwFlags & DICD_EDIT )
  638.     {
  639.         // Update the device ownership 
  640.         if( FAILED( hr = UpdateDeviceOwnership() ) )
  641.         {
  642.             DXTRACE_ERR( TEXT("UpdateDeviceOwnership"), hr );    
  643.             goto LCleanup;
  644.         }
  645.  
  646.         // Now save the device keys that are assigned players to registry, 
  647.         if( FAILED( hr = SaveDeviceOwnershipKeys() ) )
  648.         {
  649.             DXTRACE_ERR( TEXT("SaveDeviceOwnershipKeys"), hr );
  650.             goto LCleanup;
  651.         }
  652.  
  653.         // Report an error if there is a player that doesn't not
  654.         // have a device assigned
  655.         if( FAILED( hr = VerifyAssignment() ) )
  656.             goto LCleanup;
  657.         
  658.         // Now that every player has at least one device, build and set 
  659.         // the action map, and use callback into the app to tell the 
  660.         // app of the device assignment.
  661.         if( FAILED( hr = AddAssignedDevices( FALSE ) ) )
  662.         {
  663.             DXTRACE_ERR( TEXT("AddAssignedDevices"), hr );
  664.             goto LCleanup;
  665.         }
  666.     }
  667.     
  668.     hr = S_OK;
  669.  
  670. LCleanup:
  671.  
  672.     if( strUserNames )
  673.         delete [] strUserNames;
  674.  
  675.     return hr;
  676. }
  677.  
  678.  
  679.  
  680.  
  681. //-----------------------------------------------------------------------------
  682. // Name: UpdateDeviceOwnership
  683. // Desc:
  684. //-----------------------------------------------------------------------------
  685. HRESULT CMultiplayerInputDeviceManager::UpdateDeviceOwnership()
  686. {
  687.     DWORD   iPlayer;
  688.     DWORD   iDevice;
  689.  
  690.     // Set all players to not have a device yet
  691.     for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
  692.         m_pUsers[iPlayer]->bFoundDeviceForPlayer = FALSE;
  693.  
  694.     // Set all devices as not assigned to a player
  695.     for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  696.         m_pDeviceList[iDevice].pPlayerInfo = NULL;
  697.  
  698.     UnacquireDevices();
  699.     
  700.     // Update the device ownership by quering the DIPROP_USERNAME property
  701.     for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  702.     {
  703.         LPDIRECTINPUTDEVICE8 pdidDevice = m_pDeviceList[iDevice].pdidDevice;
  704.  
  705.         TCHAR strPlayerName[MAX_PATH];
  706.         DIPROPSTRING dips;
  707.         dips.diph.dwSize       = sizeof(DIPROPSTRING); 
  708.         dips.diph.dwHeaderSize = sizeof(DIPROPHEADER); 
  709.         dips.diph.dwObj        = 0; // device property 
  710.         dips.diph.dwHow        = DIPH_DEVICE;                     
  711.         pdidDevice->GetProperty( DIPROP_USERNAME, &dips.diph );                    
  712.         DXUtil_ConvertWideStringToGenericCch( strPlayerName, dips.wsz, MAX_PATH );                    
  713.  
  714.         if( lstrcmp( strPlayerName, TEXT("") ) != 0 )
  715.         {
  716.             m_pDeviceList[iDevice].pPlayerInfo = LookupPlayer( strPlayerName );
  717.             if( m_pDeviceList[iDevice].pPlayerInfo )
  718.                 m_pDeviceList[iDevice].pPlayerInfo->bFoundDeviceForPlayer = TRUE;
  719.         }
  720.     }
  721.  
  722.     return S_OK;
  723. }
  724.  
  725.  
  726.  
  727.  
  728. //-----------------------------------------------------------------------------
  729. // Name: UnacquireDevices
  730. // Desc:
  731. //-----------------------------------------------------------------------------
  732. VOID CMultiplayerInputDeviceManager::UnacquireDevices()
  733. {
  734.     // Unacquire every device
  735.  
  736.     if( m_pDeviceList )
  737.     {
  738.         // All devices have been assigned a to a user in 
  739.         // the new array, so clean up the local array
  740.         for( DWORD iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  741.         {
  742.             LPDIRECTINPUTDEVICE8 pdidDevice = m_pDeviceList[iDevice].pdidDevice;
  743.             
  744.             // Set the device's coop level
  745.             pdidDevice->Unacquire();
  746.         }
  747.     }
  748. }
  749.  
  750.  
  751.  
  752.  
  753. //-----------------------------------------------------------------------------
  754. // Name: SetFocus
  755. // Desc: Sets the DirectInput focus to a new HWND
  756. //-----------------------------------------------------------------------------
  757. VOID CMultiplayerInputDeviceManager::SetFocus( HWND hWnd ) 
  758. {
  759.     m_hWnd = hWnd;
  760.     
  761.     if( m_pDeviceList )
  762.     {
  763.         // All devices have been assigned a to a user in 
  764.         // the new array, so clean up the local array
  765.         for( DWORD iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  766.         {
  767.             LPDIRECTINPUTDEVICE8 pdidDevice = m_pDeviceList[iDevice].pdidDevice;
  768.  
  769.             // Set the device's coop level
  770.             pdidDevice->Unacquire();
  771.             pdidDevice->SetCooperativeLevel( m_hWnd, DISCL_NONEXCLUSIVE|DISCL_FOREGROUND );
  772.         }
  773.     }
  774. }
  775.  
  776.  
  777.  
  778.  
  779. //-----------------------------------------------------------------------------
  780. // Name: GetDevices
  781. // Desc: returns an array of DeviceInfo*'s
  782. //-----------------------------------------------------------------------------
  783. HRESULT CMultiplayerInputDeviceManager::GetDevices( DeviceInfo** ppDeviceInfo, 
  784.                                          DWORD* pdwCount )
  785. {
  786.     if( NULL==ppDeviceInfo || NULL==pdwCount )
  787.         return E_INVALIDARG;
  788.     
  789.     (*ppDeviceInfo) = m_pDeviceList;
  790.     (*pdwCount)     = m_dwNumDevices;
  791.     
  792.     return S_OK;
  793. }
  794.  
  795.  
  796.  
  797.  
  798. //-----------------------------------------------------------------------------
  799. // Name: LookupPlayer
  800. // Desc: searchs m_pUsers by player name
  801. //-----------------------------------------------------------------------------
  802. CMultiplayerInputDeviceManager::PlayerInfo* CMultiplayerInputDeviceManager::LookupPlayer( TCHAR* strPlayerName )
  803. {
  804.     for( DWORD iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
  805.     {
  806.         PlayerInfo* pCurPlayer = m_pUsers[iPlayer];
  807.         if( lstrcmp( pCurPlayer->strPlayerName, strPlayerName ) == 0 )
  808.             return pCurPlayer;
  809.     }
  810.     
  811.     return NULL;
  812. }
  813.  
  814.  
  815.  
  816.  
  817. //-----------------------------------------------------------------------------
  818. // Name: SaveDeviceOwnershipKeys
  819. // Desc: For every device that's assigned to a player, save its device key 
  820. //       and assigned player to registry.
  821. //-----------------------------------------------------------------------------
  822. HRESULT CMultiplayerInputDeviceManager::SaveDeviceOwnershipKeys()
  823. {
  824.     TCHAR strDeviceGuid[40];
  825.     DWORD iDevice;
  826.  
  827.     for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  828.     {                
  829.         PlayerInfo* pPlayerInfo = m_pDeviceList[iDevice].pPlayerInfo;           
  830.  
  831.         DXUtil_ConvertGUIDToStringCch( &m_pDeviceList[iDevice].didi.guidInstance, strDeviceGuid, 40 );
  832.  
  833.         if( pPlayerInfo != NULL )
  834.             DXUtil_WriteStringRegKey( m_hKey, strDeviceGuid, pPlayerInfo->strPlayerName );        
  835.         else
  836.             RegDeleteValue( m_hKey, strDeviceGuid );
  837.     }                              
  838.  
  839.     return S_OK;
  840. }
  841.  
  842.  
  843.  
  844.  
  845. //-----------------------------------------------------------------------------
  846. // Name: DeleteDeviceOwnershipKeys
  847. // Desc: Delete all the ownership keys
  848. //-----------------------------------------------------------------------------
  849. VOID CMultiplayerInputDeviceManager::DeleteDeviceOwnershipKeys()
  850. {
  851.     HKEY hKey;
  852.     TCHAR *strRegKey;
  853.  
  854.     // Prepare strings to delete the key
  855.     strRegKey = _tcsdup( m_strKey );
  856.     if( strRegKey == NULL )
  857.         return;
  858.  
  859.     TCHAR* strTemp = _tcsrchr( strRegKey, TEXT('\\') );
  860.     
  861.     // Unless the registry path string was malformed, we're ready to delete
  862.     // and recreate the key
  863.     if( strTemp ) 
  864.     {
  865.         *strTemp = 0;
  866.         strTemp++;
  867.  
  868.         RegCloseKey( m_hKey );
  869.  
  870.         // Delete the reg key
  871.         RegOpenKey( HKEY_CURRENT_USER, strRegKey, &hKey );
  872.         RegDeleteKey( hKey, strTemp );
  873.         RegCloseKey( hKey );
  874.     
  875.         // Create the key again now that all the subkeys have been deleted
  876.         RegCreateKeyEx( HKEY_CURRENT_USER, m_strKey, 0, NULL,
  877.                         REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, 
  878.                         &m_hKey, NULL );
  879.     }
  880.  
  881.  
  882.     // Clean up memory allocation
  883.     free( strRegKey );
  884. }
  885.  
  886.  
  887.  
  888.  
  889. //-----------------------------------------------------------------------------
  890. // Name: Cleanup
  891. // Desc:
  892. //-----------------------------------------------------------------------------
  893. VOID CMultiplayerInputDeviceManager::Cleanup()
  894. {
  895.     CleanupDeviceList();
  896.     
  897.     if( m_pUsers )
  898.     {
  899.         for( DWORD iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
  900.             SAFE_DELETE( m_pUsers[iPlayer] );
  901.         SAFE_DELETE( m_pUsers );
  902.     }
  903.     
  904.     // Release() base object
  905.     SAFE_RELEASE( m_pDI );
  906.     
  907. }
  908.     
  909.  
  910.  
  911.  
  912. //-----------------------------------------------------------------------------
  913. // Name: CleanupDeviceList
  914. // Desc: Clean up the device array
  915. //-----------------------------------------------------------------------------
  916. VOID CMultiplayerInputDeviceManager::CleanupDeviceList()
  917. {
  918.     for( DWORD iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  919.         SAFE_RELEASE( m_pDeviceList[iDevice].pdidDevice );
  920.     free( m_pDeviceList );
  921.     m_pDeviceList = NULL;
  922.     m_dwMaxDevices = 0;
  923.     m_dwNumDevices = 0;    
  924. }
  925.  
  926.  
  927.  
  928.  
  929.